package za.co.trojanc.ini_lib;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * A class representing a section in a ini File
 * @author Charl Thiem
 * @since 09 Sep 2009
 */
public class INISection {
	/** Mapping of fields in this section */
	private Map<String,INIField> fields;
	/** List of field names, used to keep track of field indices */
	private List<String> fieldName;
	/** Name of this section */
	private String name;
	/** Text that preceded this section in the ini file */
	private String preText = null;
	
	/**
	 * Creates a new instance of <code>INISection</code>.
	 * @param Name of this section
	 */
	public INISection(String name) {
		this.name = name;
		this.fields = new HashMap<String, INIField>();
		this.fieldName = new ArrayList<String>();
	}
	
	/**
	 * Gets the name of this <code>INISection</code>.
	 * @return Name of this field.
	 */
	public String getName() {
		return this.name;
	}
	
	/**
	 * Adds an field to this ini section
	 * @param name Name of the field.
	 * @param value Value for this field.
	 */
	public void addField(String name, String value) {
		this.addField(new INIField(name, value));
	}
	
	/**
	 * Adds a field to this section
	 * @param field Field to add.
	 */
	public void addField(INIField field) {
		this.fields.put(field.getName(), field);
		this.fieldName.add(field.getName());
	}
	
	/**
	 * Gets a field with the specified name.
	 * @param name Name of the field to find.
	 * @return The INIField.
	 */
	public INIField getField(String name) {
		return this.fields.get(name);
	}

	/**
	 * Sets the text that preceded this section in the ini file.
	 * @param text Text.
	 */
	public void setPreText(String text) {
		this.preText = text;
	}
	
	/**
	 * Gets the text that preceded this section in the ini file.
	 * @return Text.
	 */
	public String getPreText() {
		return this.preText;
	}
	
	/**
	 * Returns an array of names of fields in the order they are defined.
	 * @return Array of field names.
	 */
	public String[] getFieldNames() {
		return this.fieldName.toArray(new String[0]);
	}
	
	/**
	 * Returns true if this section contains a field with the specified name.
	 * @param fieldName Name of the field to search for.
	 * @return True if the field exists.
	 */
	public boolean hasFieldNamed(String fieldName) {
		return this.fieldName.contains(fieldName);
	}
	
	/**
	 * Gets the value of a field.
	 * @param fieldName Name of field to get.
	 * @param defaultValue Default value if field has no value.
	 * @return The value of the field or the default value.
	 */
	public String getStringValue(final String fieldName, final String defaultValue) {
		INIField field = this.getField(fieldName);
		if (field == null) {
			return defaultValue;
		}
		else {
			return field.getStringValue(defaultValue);
		}
	}
	
	/**
	 * Gets the int value of a field.
	 * @param fieldName Name of the field to get as an int.
	 * @param defaultValue Value if field is not found or does not represent an int.
	 * @return
	 */
	public int getIntValue(String fieldName, int defaultValue) {
		INIField field = this.getField(fieldName);
		if (field == null) {
			return defaultValue;
		}
		else {
			return field.getIntValue(defaultValue);
		}
	}
	
	/**
	 * Gets the long value of a field.
	 * @param fieldName Name of the field to get as an long.
	 * @param defaultValue Value if field is not found or does not represent a long.
	 * @return
	 */
	public long getLongValue(String fieldName, long defaultValue) {
		INIField field = this.getField(fieldName);
		if (field == null) {
			return defaultValue;
		}
		else {
			return field.getLongValue(defaultValue);
		}
	}
	
	/**
	 * Sets/Adds a field in this <code>INISection<code>.
	 * @param fieldName Name of the field to set.
	 * @param value Value for this field.
	 */
	public void setField(String fieldName, String value) {
		if (this.hasFieldNamed(fieldName)) {
			INIField field = this.getField(fieldName);
			field.setValue(value);
		}
		else {
			this.addField(fieldName, value);
		}
	}
	
	/**
	 * Returns an array of fields in the order they are defined.
	 * @return Array of INIFields
	 */
	public INIField[] getFields() {
		List<INIField> flds = new ArrayList<INIField>();
		String[] names = this.getFieldNames();
		for(String n : names) {
			flds.add(this.getField(n));
		}
		return flds.toArray(new INIField[0]);
	}
	
	/**
	 * Gets a list of field from this section.
	 * <BR><BR>
	* <code>
	 * student1.name=Leon<BR>
	 * student1.surname=Smith<BR>
	 * student2.name=Mia<BR>
	 * student2.surname=Liebenberg
	 * </code>
 	 * <BR><BR>
 	 * <code>getListFields("student%i%.name");</code><BR>
	 * <code>INIField[0]</code> = Leon<BR>
	 * <code>INIField[1]</code> = Mia<BR>
	 * 
	 * @param fieldName
	 * @return
	 */
	public INIField[] getListFields( final String fieldName){
		List<INIField> fields = new ArrayList<INIField>();
		
		int index = 1;
		INIField field = this.getField(makeName(fieldName, index));
		while (field != null){
			fields.add(field);
			field = this.getField(makeName(fieldName, ++index));
		}
		return fields.toArray(new INIField[fields.size()]);
	}
	
	/**
	 * Gets a list of field from this section.
	 * <BR><BR>
	 * The second dimension of the array will be in the order in which the field
	 * names are given.
	 * <BR><BR>
	 * <code>INIField[0][0]</code> = First instance's first field<BR>
	 * <code>INIField[0][1]</code> = First instance's second field<BR>
	 * <code>INIField[0][0]</code> = First instance's N'th field<BR>
	 * <code>INIField[X][N]</code> = X'th instance's N'th field.
	 * <BR><BR>
	 * Eg:<BR>
	 * <code>
	 * <font color=green>student1.name</font>=Leon<BR>
	 * <font color=green>student1.surname</font>=Smith<BR>
	 * <font color=green>student1.age</font>=23<BR>
	 * <font color=green>student2.name</font>=Mia<BR>
	 * <font color=green>student2.surname</font>=Liebenberg
	 * </code>
 	 * <BR><BR>
 	 * <code> String[] fieldNames    = {"student%i%.name", "student%i%.surname", "student%i%.age"}</code><BR>
 	 * <code> Object[] defaultValues = {"NoName", "NoSurname", new Integer(0)}</code><BR>
 	 * <code> Class[] type = {String.class, String.class, Integer.class}</code><BR>
 	 * <BR>
 	 * <code>Object[][] results = getListFields(fieldNames,defaultValues,type);</code><BR>
	 * <code>results[0][0]</code> = Leon<BR>
	 * <code>results[0][1]</code> = Smith<BR>
	 * <code>results[0][2]</code> = 23<BR>
	 * <code>results[1][0]</code> = Mia<BR>
	 * <code>results[1][1]</code> = Liebenberg<BR>
	 * <code>results[1][2]</code> = 0<BR>
	 * @param fieldNames An array of field name (with index markers "%i%)
	 * @param defaultValues Default values for each field name if the field is not found.
	 * @param type The class type of each field.
	 * @return The 2-dimensional array of results in the order as explained above.
	 * The value of each field will be in the class type as specified.
	 */
	@SuppressWarnings("unchecked")
	public Object[][] getListFields(final String[]fieldNames, Object[] defaultValues, Class[]type){
		
		List<INIField[]> listing = new ArrayList<INIField[]>();
		
		for (String fieldName : fieldNames){
			listing.add(this.getListFields(fieldName));
		}
		/*
		 * Right now listing's size is the same as fieldNames.length
		 * Each INIField[] in listing now contains the field value
		 * for each instance of the finalList. ie. the first item
		 * in the list contains an array of names, the second item in 
		 * the list is an array or surnames
		 */
		
		INIField[] list;
		Object[][] finalList;
		Object currentValue;
		/* To find out how many instances there are, peek to the first item 
		 * in the listing as this will contain all the names, therefore telling
		 * us how many instances we can expect
		 */
		final int NUM_INSTANCES = listing.get(0).length;
		finalList = new Object[NUM_INSTANCES][fieldNames.length];
		
		/*
		 * Now add the fields in each array from listing, to each instance.
		 */
		for (int y = 0 ; y < listing.size() ; y++){
			list = listing.get(y);
			for(int x = 0 ; x < NUM_INSTANCES ; x++){
				currentValue = list[x].getValueAs(type[y], defaultValues[y]);
				finalList[x][y] = (Object)currentValue;
			}
		}
		
		return finalList;
	}

	/**
	 * Replaces %i% with the specified index
	 * @param name 
	 * @param index
	 * @return
	 */
	public static final String makeName(String name, int index) {
		boolean hasModifier = name.contains("%i%");
		if (hasModifier) {
			return name.replaceAll("\\%i\\%", Integer.toString(index));
		}
		else {
			return name + index;
		}
	}
	/**
	 * Returns the value as a boolean that was either 'true' or 'false' (case insensitive)
	 * @param fieldName Name of the field to get the boolean value of.
	 * @param defaultValue Default value if the field is not found
	 * @return The value of the field, <code>defaultValue</code> if the value is not found.
	 */
	public boolean getBooleanValue(String fieldName, boolean defaultValue) {
		String value = this.getStringValue(fieldName, null);
		if (value == null) {
			return defaultValue;
		}
		else { 
			return Boolean.parseBoolean(value);
		}
	}
}
